// 版权所有2012 Go作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// 此文件实现转换的类型检查。

package types2

import (
	"go/constant"
	"unicode"
)

// 转换类型检查转换T（x）。
// 结果是x。
func (check *Checker) conversion(x *operand, T Type) {
	constArg := x.mode == constant_

	var ok bool
	switch {
	case constArg && isConstType(T):
		// 恒定转换
		switch t := asBasic(T); {
		case representableConst(x.val, check, t, &x.val):
			ok = true
		case isInteger(x.typ) && isString(t):
			codepoint := unicode.ReplacementChar
			if i, ok := constant.Uint64Val(x.val); ok && i <= unicode.MaxRune {
				codepoint = rune(i)
			}
			x.val = constant.MakeString(string(codepoint))
			ok = true
		}
	case x.convertibleTo(check, T):
		// 非常数转换
		x.mode = value
		ok = true
	}

	if !ok {
		if x.mode != invalid {
			check.errorf(x, "cannot convert %s to %s", x, T)
			x.mode = invalid
		}
		return
	}

	// 转换参数类型是最终的。对于非类型化的值
	// 转换根据规范提供类型：“常数可能是
	// 通过常量声明或转换显式给定类型，…”。
	if isUntyped(x.typ) {
		final := T
		// -对于到接口的转换，除了非类型化的nil参数，
		// 使用参数的默认类型。
		// -用于将非类型化常量转换为非常量类型
		// 使用默认类型（例如[]字节（“foo”）应报告字符串
		// 不是[]字节作为常量“foo”的类型。
		// -对于整数到字符串的转换，保留参数类型。
		// （另请参见下面的待办事项。）
		if x.typ == Typ[UntypedNil] {
			// 好啊
		} else if IsInterface(T) || constArg && !isConstType(T) {
			final = Default(x.typ)
		} else if isInteger(x.typ) && isString(T) {
			final = x.typ
		}
		check.updateExprType(x.expr, final, true)
	}

	x.typ = T
}

// TODO（gri）敞篷车检查T（x）是否有效。它假定
// x的值是完全已知的，但对于say字符串（1<<s+1.0）则不是这样：
// 这里，1<<s+1.0的类型将是非类型的float，这将导致
// （正确！）拒绝转换。但报告的错误本质上是错误的
// “无法将非类型化的浮点值转换为字符串”，但返回了正确的错误（根据
// 规范）规定我们不能移动浮点值：1/1应该
// 由于添加了1.0，无法转换为非类型的float。修理这个
// 这很棘手，因为我们必须首先对参数运行updateExprtType。
// （第21982期）

// convertibleTo报告T（x）是否有效。
// 如果通过
// 导出的API调用，即当所有方法都已进行类型检查时。
func (x *operand) convertibleTo(check *Checker, T Type) bool {
	// “x可分配给T”
	if ok, _ := x.assignableTo(check, T, nil); ok {
		return true
	}

	// “如果忽略标记，则x的类型和T具有相同的基础类型”
	V := x.typ
	Vu := under(V)
	Tu := under(T)
	if check.identicalIgnoreTags(Vu, Tu) {
		return true
	}

	// “x的类型和T是未命名的指针类型及其指针基类型
	// 如果忽略标记，则具有相同的基础类型”
	if V, ok := V.(*Pointer); ok {
		if T, ok := T.(*Pointer); ok {
			if check.identicalIgnoreTags(under(V.base), under(T.base)) {
				return true
			}
		}
	}

	// “x的类型和T都是整数或浮点类型”
	if isIntegerOrFloat(V) && isIntegerOrFloat(T) {
		return true
	}

	// “x的类型和T都是复杂类型”
	if isComplex(V) && isComplex(T) {
		return true
	}

	// “x是一个整数或字节片或符文，T是字符串类型”
	if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) {
		return true
	}

	// “x是字符串，T是字节或符文的片段”
	if isString(V) && isBytesOrRunes(Tu) {
		return true
	}

	// 包装不安全：
	// “基础类型uintptr的任何指针或值都可以转换为不安全的.指针”
	if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) {
		return true
	}
	// “反之亦然”
	if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) {
		return true
	}

	// “x是一个片，T是指向数组类型的指针，
	// 切片和数组类型具有相同的元素类型。”
	if s := asSlice(V); s != nil {
		if p := asPointer(T); p != nil {
			if a := asArray(p.Elem()); a != nil {
				if check.identical(s.Elem(), a.Elem()) {
					if check == nil || check.allowVersion(check.pkg, 1, 17) {
						return true
					}
					// 检查！=无
					if check.conf.CompilerErrorMessages {
						check.error(x, "conversion of slices to array pointers only supported as of -lang=go1.17")
					} else {
						check.error(x, "conversion of slices to array pointers requires go1.17 or later")
					}
					x.mode = invalid // 避免后续错误
				}
			}
		}
	}

	return false
}

func isUintptr(typ Type) bool {
	t := asBasic(typ)
	return t != nil && t.kind == Uintptr
}

func isUnsafePointer(typ Type) bool {
	// TODO（gri）：这是基本（典型）而不是典型（*基本）正确吗？
	// （前者在（）下调用，而后者不调用。）
	// 规范没有这样说，但gc声称确实如此。另见
	// 第6326期。
	t := asBasic(typ)
	return t != nil && t.kind == UnsafePointer
}

func isPointer(typ Type) bool {
	return asPointer(typ) != nil
}

func isBytesOrRunes(typ Type) bool {
	if s := asSlice(typ); s != nil {
		t := asBasic(s.elem)
		return t != nil && (t.kind == Byte || t.kind == Rune)
	}
	return false
}
